/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib;

import java.util.Arrays;

import ac.jp.u_tokyo.SyncLib.StateSync.RevertMethod;

public abstract class StaticCombinatorBase extends StateSync
{
	private static class SyncInvokeMethod extends RevertMethod
	{

		@Override
		public Object revert(Object current)
		{
			Sync s = (Sync) current;
			s.revert();
			return s;
		}

		@Override
		public Object cancelRevertPoint(Object current)
		{
			Sync s = (Sync) current;
			s.cancelRevertPoint();
			return s;
		}

	}

	int _syncStartingIndex;

	int _syncCount;

	boolean[] _syncInvoked;

	protected Sync getSync(int index)
	{
		return (Sync) _values[_syncStartingIndex + index];
	}

	protected int getSyncCount() {
		return _syncCount;
	}

	public StaticCombinatorBase(Object[] attributes, Sync[] syncs)
	{
		super(concatenate(attributes, syncs));
		_syncStartingIndex = attributes.length;
		_syncCount = syncs.length;
		_syncInvoked = new boolean[_syncCount];
	}

	private static Object[] concatenate(Object[] array1, Object[] array2)
	{
		Object[] result = new Object[array1.length + array2.length];
		for (int i = 0; i < array1.length; i++)
		{
			result[i] = array1[i];
		}
		for (int i = 0; i < array2.length; i++)
		{
			result[i + array1.length] = array2[i];
		}
		return result;
	}

	protected final Mod[] invokeSync(int index, Mod[] mods)
			throws SynchronizationFailedException
	{
		Sync s = getSync(index);
		if (_syncInvoked[index] == false) {
			s.setRevertPoint();
			_syncInvoked[index] = true;
		}
		return s.synchronize(mods);
	}

	protected final Mod[] invokeResync(int index, Object[] values, Mod[] mods)
			throws SynchronizationFailedException
	{
		Sync s = getSync(index);
		if (_syncInvoked[index] == false) {
			s.setRevertPoint();
			_syncInvoked[index] = true;
		}
		return s.resynchronize(values, mods);
	}

	protected abstract Mod[] doResynchronize(Object[] values,
			Mod[] modifications) throws SynchronizationFailedException;

	protected void afterSuccessfulSynchronization()
	{
		if (!_revertMethods.empty())
		{
			for (int i = 0; i < _syncCount; i++)
			{
				if (_syncInvoked[i])
					if (_revertMethods.peek()[i + _syncStartingIndex] == null)
					{
						_syncInvoked[i] = false;
						_revertMethods.peek()[_syncStartingIndex + i] = new SyncInvokeMethod();
					}
			}
		}
		for (int i = 0; i < _syncCount; i++)
		{
			if (_syncInvoked[i])
			{
				getSync(i).cancelRevertPoint();
				_syncInvoked[i] = false;
			}

		}
	}

	protected void afterFailedSynchronization()
	{
		for (int i = 0; i < _syncCount; i++)
		{
			if (_syncInvoked[i])
			{
				getSync(i).revert();
				_syncInvoked[i] = false;
			}
		}

	}

	public final Mod[] resynchronize(Object[] values, Mod[] modifications)
			throws SynchronizationFailedException
	{
		assert !Arrays.toString(_syncInvoked).contains("true");
		try
		{
			Mod[] result = doResynchronize(values, modifications);
			afterSuccessfulSynchronization();
			return result;
		}
		catch (SynchronizationFailedException e)
		{
			afterFailedSynchronization();
			throw e;
		}
	}

	protected abstract Mod[] doSynchronize(Mod[] modifications)
			throws SynchronizationFailedException;

	public final Mod[] synchronize(Mod[] modifications)
			throws SynchronizationFailedException
	{
		// TODO Auto-generated method stub
		//call the actual synchronizer
		//after returning, add the first-time called syncs to the last state
		//and call the cancel() method of other syncs
		//after throwing, call the revert() method of all syncs
		assert !Arrays.toString(_syncInvoked).contains("true");
		try
		{
			Mod[] result = doSynchronize(modifications);
			afterSuccessfulSynchronization();
			return result;
		}
		catch (SynchronizationFailedException e)
		{
			afterFailedSynchronization();
			throw e;
		}
	}

}
